package com.mdd.front.controller;

import com.github.binarywang.wxpay.bean.notify.*;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.icbc.api.DefaultIcbcClient;
import com.icbc.api.IcbcApiException;
import com.icbc.api.IcbcClient;
import com.icbc.api.IcbcConstants;
import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1;
import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineMerrefundResponseV1;
import com.icbc.api.IcbcApiException;
import com.icbc.api.internal.util.internal.util.fastjson.JSON;
import com.icbc.api.utils.IcbcSignature;
import com.icbc.api.utils.WebUtils;
import com.mdd.common.core.AjaxResult;
import com.mdd.common.entity.order.Order;
import com.mdd.common.utils.WxPayServiceUtils;
import com.mdd.front.LikeFrontThreadLocal;
import com.mdd.front.service.pay.ICBCService;
import com.mdd.front.constant.YmlConstant;
import com.mdd.front.service.pay.ICBCService;
import com.mdd.front.service.pay.IWxpayService;
import com.mdd.front.service.order.IOrderService;
import com.mdd.front.service.pay.impl.ICBCServiceImpl;
import com.mdd.front.service.recharge.IRechargeService;
import com.mdd.front.service.refund.IOrderRefundService;
import com.mdd.front.validate.BalancePayValidate;
import com.mdd.front.validate.PaymentValidate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author panweiliang
 */
@Slf4j
@RestController
@RequestMapping("/api/pay/wx")
public class WxPayController {

	@Value("${icbc.publicKey}")
	private String icbcPublicKey;

	@Value("${icbc.privateKey}")
	private String icbcPrivateKey;

	@Resource
	private IWxpayService wxPayService;

	@Resource
	private IOrderService orderService;
	@Resource
	private IRechargeService iRechargeService;

	@Resource
	private IOrderRefundService refundService;

	@Resource
	private ICBCService icbcService;

	/**
	 * <pre>
	 * 查询订单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2)
	 * 该接口提供所有微信支付订单的查询，商户可以通过查询订单接口主动查询订单状态，完成下一步的业务逻辑。
	 * 需要调用查询接口的情况：
	 * ◆ 当商户后台、网络、服务器等出现异常，商户系统最终未接收到支付通知；
	 * ◆ 调用支付接口后，返回系统错误或未知交易状态情况；
	 * ◆ 调用被扫支付API，返回USERPAYING的状态；
	 * ◆ 调用关单或撤销接口API之前，需确认支付状态；
	 * 接口地址：https://api.mch.weixin.qq.com/pay/orderquery
	 * </pre>
	 * <p>
	 * 查询订单支付信息
	 *
	 * @param transactionId 微信订单号
	 * @param outTradeNo    商户系统内部的订单号，当没提供transactionId时需要传这个。
	 */
	@GetMapping("/queryOrder")
	public WxPayOrderQueryV3Result queryOrder(@RequestParam(required = false) String transactionId,
	                                          @RequestParam(required = false) String outTradeNo)
			throws WxPayException {
		Order order = orderService.getBySn(outTradeNo);
		return WxPayServiceUtils.wxPayService(order.getPayChannel()).queryOrderV3(transactionId, outTradeNo);
	}

	/**
	 * <pre>
	 * 关闭订单
	 * 应用场景
	 * 以下情况需要调用关单接口：
	 * 1. 商户订单支付失败需要生成新单号重新发起支付，要对原订单号调用关单，避免重复支付；
	 * 2. 系统下单后，用户支付超时，系统退出不再受理，避免用户继续，请调用关单接口。
	 * 注意：订单生成后不能马上调用关单接口，最短调用时间间隔为5分钟。
	 * 接口地址：https://api.mch.weixin.qq.com/pay/closeorder
	 * 是否需要证书：   不需要。
	 * </pre>
	 *
	 * @param outTradeNo 商户系统内部的订单号
	 *                   关闭订单
	 */
	@GetMapping("/closeOrder")
	public void closeOrder(@RequestParam("outTradeNo") String outTradeNo) throws Exception {
		Order order = orderService.getBySn(outTradeNo);
		WxPayServiceUtils.wxPayService(order.getPayChannel()).closeOrderV3(outTradeNo);
	}


	/**
	 * 调用统一下单接口，并组装生成支付所需参数对象.
	 *
	 * @param paymentValidate 支付参数
	 * @return 支付结果
	 * @throws WxPayException 支付异常信息
	 */
	@PostMapping("/prePay")
	public AjaxResult prePay(@Validated(value = PaymentValidate.create.class)
	                         @RequestBody PaymentValidate paymentValidate) throws WxPayException {
		Object pay = wxPayService.prePay(paymentValidate);
		return AjaxResult.success(pay);
	}


	/**
	 * 该方法是支付成功后icbc的回调函数
	 * @param req 请求request
	 * @param resp 响应response
	 */
	@RequestMapping("/icbc/notifyPayCall")
	public void ICBCPayCallback(HttpServletRequest req, HttpServletResponse resp) {
		PrintWriter out = null;
		log.info("支付回调开始==========");
		try {
			Map<String, String> params = new HashMap<String, String>();
			String from = req.getParameter("from");
			String api = req.getParameter("api");
			String app_id = req.getParameter("app_id");
			String charset = req.getParameter("charset");
			String format = req.getParameter("format");
			String encrypt_type = req.getParameter("encrypt_type");
			String timestamp = req.getParameter("timestamp");
			String biz_content = req.getParameter("biz_content");
			String sign_type = req.getParameter("sign_type");
			String sign = req.getParameter("sign");
			params.put("from", from);
			params.put("api", api);
			params.put("app_id", app_id);
			params.put("charset", charset);
			params.put("format", format);
			params.put("encrypt_type", encrypt_type);
			params.put("timestamp", timestamp);
			params.put("biz_content", biz_content);
			params.put("sign_type", sign_type);//目前上行网关签名暂时仅支持RSA
			log.info("ICBC请求的参数是： {}", params);
			Map<String, String> respMap = (Map<String, String>) JSON.parse(biz_content);
			log.info("异步请求返回的biz_content的是： {}", respMap);
			// 进行业务操作
			orderService.updateOrderStatusToICBC(respMap);

			String path = "/api/pay/wx/icbc/notifyPayCall";
			String signStr = WebUtils.buildOrderedSignStr(path, params);
			String results = null;
			String responseBizContent = null;
			boolean flag = false;
			flag = IcbcSignature.verify(signStr, sign_type, this.icbcPublicKey, charset, sign);
			if (!flag) {
				responseBizContent = "{\"return_code\":-12345,\"return_msg\":\"icbc sign not pass.\"}";
			} else {
				String msg_id = respMap.get("msg_id").toString();
				int return_code = 0;
				String return_msg = "success.";
				responseBizContent = "{\"return_code\":" + return_code + ",\"return_msg\":\"" + return_msg + "\",\"msg_id\":\"" + msg_id + "\","
						+ "\"busi_param_rp\":\"thisisresponseparameter\"}";
				signStr = "\"response_biz_content\":" + responseBizContent + "," + "\"sign_type\":" + "\"RSA\"";
				sign = IcbcSignature.sign(signStr, "RSA", icbcPrivateKey,
						charset, "test-不知道什么意思");
				results = "{" + signStr + ",\"sign\":\"" + sign + "\"}";
				out = resp.getWriter();
				out.write(results);
			}
		} catch (IcbcApiException e) {
			log.error("icbc 返回支付请求消息加密或解密失败", e);
		} catch (IOException e) {
			log.error("消息响应失败{}", e);
		} catch (Exception e) {
			log.error("系统异常， 订单服务错误", e);
		}
	}


	//  支付回调通知处理
	@PostMapping("/notify/order")
	public String parseOrderNotifyResult(@RequestBody String xmlData, HttpServletRequest request) throws WxPayException {
		log.info("支付回调通知的处理,原生xml数据,xmlData:{}", xmlData);
		SignatureHeader signatureHeader = this.getRequestHeader(request);
		final WxPayOrderNotifyV3Result.DecryptNotifyResult notifyResult = WxPayServiceUtils.wxPayService(null).parseOrderNotifyV3Result(xmlData, signatureHeader).getResult();
		log.info("支付回调通知的处理,解析后数据,parseOrderNotifyResult:{}", notifyResult);
		String transactionId = notifyResult.getTransactionId();
		String outTradeNo = notifyResult.getOutTradeNo();
		boolean flag = this.orderService.updateOrderStatusToPaid(outTradeNo, transactionId);
		return flag ? WxPayNotifyResponse.success("成功") : WxPayNotifyResponse.fail("失败");
	}

	//  支付回调通知处理
	@PostMapping("/notify/rechargeOrder")
	public String parseRechargeOrderNotifyResult(@RequestBody String xmlData, HttpServletRequest request) throws WxPayException {
		log.info("支付回调通知处理,原生xml数据,xmlData:{}", xmlData);
		SignatureHeader signatureHeader = this.getRequestHeader(request);
		final WxPayOrderNotifyV3Result.DecryptNotifyResult notifyResult = WxPayServiceUtils.wxPayService(null).parseOrderNotifyV3Result(xmlData, signatureHeader).getResult();
		log.info("支付回调通知处理,解析后数据,parseOrderNotifyResult:{}", notifyResult);
		String transactionId = notifyResult.getTransactionId();
		String outTradeNo = notifyResult.getOutTradeNo();
		boolean flag = this.iRechargeService.updateOrderStatusToPaid(outTradeNo, transactionId);
		return flag ? WxPayNotifyResponse.success("成功") : WxPayNotifyResponse.fail("失败");
	}

	//  退款回调通知处理
	@PostMapping("/notify/refund")
	public String parseRefundNotifyResult(@RequestBody String xmlData, HttpServletRequest request) throws WxPayException {
		log.info("退款回调通知处理原生数据,xmlData:{}", xmlData);
		SignatureHeader signatureHeader = this.getRequestHeader(request);
		final WxPayRefundNotifyV3Result.DecryptNotifyResult result = WxPayServiceUtils.wxPayService(null).parseRefundNotifyV3Result(xmlData, signatureHeader).getResult();
		log.info("退款回调通知处理,parseRefundNotifyResult:{}", result);
		boolean flag = refundService.updateOrderStatusToPaid(result);
		return flag ? WxPayNotifyResponse.success("成功") : WxPayNotifyResponse.fail("失败");
	}

	//  扫码支付回调通知处理
	@PostMapping("/notify/scanPay")
	public String parseScanPayNotifyResult(String xmlData) throws WxPayException {
		final WxScanPayNotifyResult result = WxPayServiceUtils.wxPayService(null).parseScanPayNotifyResult(xmlData);
		// TODO 根据自己业务场景需要构造返回对象
		return WxPayNotifyResponse.success("成功");
	}

	/**
	 * 获取回调请求头：签名相关
	 *
	 * @param request HttpServletRequest
	 * @return SignatureHeader
	 */
	public SignatureHeader getRequestHeader(HttpServletRequest request) {
		// 获取通知签名
		String signature = request.getHeader("wechatpay-signature");
		String nonce = request.getHeader("wechatpay-nonce");
		String serial = request.getHeader("wechatpay-serial");
		String timestamp = request.getHeader("wechatpay-timestamp");

		SignatureHeader signatureHeader = new SignatureHeader();
		signatureHeader.setSignature(signature);
		signatureHeader.setNonce(nonce);
		signatureHeader.setSerial(serial);
		signatureHeader.setTimeStamp(timestamp);
		return signatureHeader;
	}
}

